2019 Ecuador election analysis - Mayoral elections

Introduction

The focus of this analysis is to identify party performance in mayoral elections. Specifically, the analysis focuses on the parties that had won the most in these elections. Local elections took place on March 24, 2019. Some local authorities won with less than a third of support of the electorate due to the sheer number of candidates running for a single seat, which points out to a deep party fragmentation in the political system.

Background

Ecuador has a multi-party system with almost no long-standing party tradition. Multiple parties emerge every few years in a sort of political cycle, even though most politicians have been around for several decades, but with different political organizations. General elections and local elections take place every four years but with a lag of two years between each other. The last general election was in 2017, whereas the last local election was in 2019. Next general election is in 2021.

Even though the multi-party system is hailed as an expression of democracy, it is clear that there is a significant fragmentation in the political system. There were more than 40 000 candidates for the 5 661 available seats for local authorities. That is an average of seven candidates for each seat. The following analysis focuses only on the elections for mayor in the 221 city councils in the country.

Analysis

Exploration

# loading libraries
library(tidyverse)
library(plotly)

# loading data for political parties
load("parties.Rdata")
partidos_alc <- partidos_alc %>%
  mutate(OP_SIGLAS = as.factor(toupper(as.character(OP_SIGLAS)))) #OP_SIGLAS - acronyms for parties
head(partidos_alc)
## # A tibble: 6 x 5
##   OP_CODIGO CANDIDATO_CODIGO CANDIDATO_ESTADO OP_NOMBRE                OP_SIGLAS
##       <dbl>            <dbl> <fct>            <chr>                    <fct>    
## 1         3                2 Electos          Partido Sociedad Patrio~ PSP      
## 2        37                3 No Electos       Movimiento Sociedad Uni~ SUMA     
## 3        31                9 Electos          Movimiento Peninsular C~ MPCNG    
## 4        12               10 No Electos       Movimiento De Unidad Pl~ MUPP     
## 5         3               16 No Electos       Partido Sociedad Patrio~ PSP      
## 6       210               17 No Electos       Movimiento De Accion Ci~ MACF
# number of candidates
partidos_alc %>%
  select(CANDIDATO_CODIGO) %>% #CANDIDATO_CODIGO - id for each candidate
  summarise(total.count = n())
## # A tibble: 1 x 1
##   total.count
##         <int>
## 1        1875
# number of parties
partidos_alc %>%
  select(OP_CODIGO) %>% #OP_CODIGO - id for each party
  unique() %>%
  tally()
## # A tibble: 1 x 1
##       n
##   <int>
## 1   380

There are 1 875 candidates from 380 parties for the mayoral elections. That means that there was an average of almost 8.5 candidates for each of the 221 cantones (city council) of the country. It is important to note that the number of parties differ if I consider either their names or their acronyms. Some parties use the same acronym, despite having different names. Some names in the data set use the generic word ‘Alianza’ (alliance, in Spanish), meaning that they do not differentiate between one another.

The variable ‘OP_CODIGO’ uses a unique id for each party. There are some cases where the same party or alliance will have a different id based on the location in which they are participating. However, this happens when the party or alliance is local, instead of regional or national. The assumption is that these parties or alliances do not have a strong performance; therefore, it will not affect the analysis of larger parties. This is confirmed by getting the same results for top parties when using both the acronyms and the unique id.

Number of victories by party or alliance

# getting 10 top individual parties and alliances
top_parties <- partidos_alc %>%
  filter(CANDIDATO_ESTADO == "Electos") %>% #filtering the elected candidates
  group_by(OP_SIGLAS) %>%
  count(OP_SIGLAS) %>%
  arrange(desc(n)) %>%
  head(10) %>% droplevels()
(round(sum(top_parties$n)/221, 2))*100
## [1] 40
top_plot <- plot_ly(top_parties, x = ~reorder(OP_SIGLAS, -n), y = ~n, 
                    marker = list(color = 'rgb(158,202,255)', opacity = rep(0.8, 10),
                                  line = list(color = 'rgb(8,48,107)', width = 1.5)),
                    type = "bar") %>%
  layout(title = "Ten best performing parties/alliances by number of mayors elected", showlegend = FALSE,
         yaxis = list(title = 'Count'),
         xaxis = list(title = 'Parties and alliances'))
top_plot

Based on the number of victories, the MUPP could be considered the most successful party, as it managed to win in 15 cantones. However, there is a problem with this assertion, as it does not take into account how powerful alliances can be. Looking at the second, fifth and eight position, it appears that the PSC beats the MUPP, as it has won in more places by forming alliances. The victories of these 10 parties and alliances represent about 40% of the cantones in the country. It is also noteworthy the alliance between six parties, which has five victories.

Number of victories by number of parties in alliance

In theory, an alliance would entail that the parties involved are sharing power. However, in practice that is not always the case. A single individual and not a council take the mayor seat. An alliance may be formed by party x and y, where the candidate for mayor comes from party x. Voters may expect that the alliance will achieve some sort of balance between two parties when governing, but it is highly likely that the candidate will almost always favor party x policies. Another issue has to do with agreements between parties, where the support for the alliance can be explicitly divided between the parties involved. As an example, an agreement may give the major party 70% of the control of local government, as it invests more resources and has a wider support in the electorate.

It is difficult to determine how each particular agreement works and it may take a whole different analysis and approach to accurately represent the balance of power in alliances. However, for the purposes of this analysis, the idea is to show how much the involvement of alliances can change the performance of parties in local elections. For this reason, the next step does not assume a division of power, but only accounts for the presence of a party in an alliance. This means that if party x and y share an alliance, both parties are registered to have won an election. This would inflate the real number of victories for each party. Nonetheless, it works as a representation of how much the party system can change and how important and widespread alliances are.

# all winning parties and alliances
alliances <- partidos_alc %>%
  mutate(parties_n = str_count(OP_SIGLAS, "/") + 1) %>% #how many parties are in the alliance
  filter(CANDIDATO_ESTADO == "Electos")

alliances %>%
  group_by(parties_n) %>%
  mutate(total_allliance = n()) %>%
  select(parties_n, total_allliance) %>% unique() %>% 
  arrange(parties_n) %>% ungroup() %>%
  mutate(perc = total_allliance/sum(total_allliance)*100)
## # A tibble: 6 x 3
##   parties_n total_allliance  perc
##       <dbl>           <int> <dbl>
## 1         1             107 48.4 
## 2         2              78 35.3 
## 3         3              17  7.69
## 4         4               6  2.71
## 5         5               8  3.62
## 6         6               5  2.26

Single parties won in 107 cantones in the country, which is little under half of all the city councils in the country. The majority of victories for mayor belong to alliances, being the alliance of two parties the most used (35.3%).

# parties per alliance
alliances <- as_tibble(str_split(alliances$OP_SIGLAS, "/", simplify = TRUE))

# number of victories of each party running alone or as a part of an alliance
alliances <- alliances %>% 
  mutate(parties_n = rowSums(alliances != "")) %>% #sums the number of parties in each election
  gather("V1","V2","V3","V4","V5","V6", key = "V", value = "party") %>% #reshape data
  filter(party != "") %>% #removes NA
  count(party, parties_n) %>% #number of times a party appears per number of alliance
  group_by(party) %>% 
  mutate(total = sum(n)) %>% 
  arrange(desc(total), desc(parties_n), desc(n)) %>% 
  ungroup()

# identifying top ten individual parties
alliances <- alliances %>%
  left_join(alliances[!duplicated(alliances$party),] %>% #modify alliances by deleting party duplicates
              select(party) %>% #takes only one variable
              mutate(id_party = row_number())) %>% #creates id to join
  filter(id_party <= 10) %>% #filters by id
  mutate(parties_n = case_when(
    .$parties_n == 1 ~ 'A single party',
    .$parties_n == 2 ~ 'Alliance of 2',
    .$parties_n == 3 ~ 'Alliance of 3',
    .$parties_n == 4 ~ 'Alliance of 4',
    .$parties_n == 5 ~ 'Alliance of 5',
    .$parties_n == 6 ~ 'Alliance of 6'
  ))


# plotting alliances
alliances_plot <- plot_ly(alliances, x = ~reorder(party, -total), y = ~n,
                          color = ~as.factor(parties_n),
                          type = 'bar',
                          text = ~total,
                          textposition = 'inside') %>%
  layout(title = 'Top ten parties by number of alliances in mayor races', showlegend = TRUE,
         barmode = 'stack',
         legend = list(orientation = 'h',
                       xanchor = 'center',
                       x = 0.5),
         yaxis = list(title = 'Count'),
         xaxis = list(title = ''))
alliances_plot

By taking into account the number of alliances, the order of the most successful parties changes significantly. The PSC could be considered the most successful party with 42 victories, 30 of them come from an alliance with one more party. The performance of the MUPP falls to fifth place, as it is the party with the less number victories in alliances (only 6), whereas the MMDG has no victory on its own, but 15 in alliances. Noteworthy are the parties with victories in alliances of five or six parties, as the implications of power sharing could be complex.

Parties in second place

Turning back to the parties and alliances in the first part of this analysis, the next question was to find out if there is a trend for the parties that came in second place. The idea is to find out which parties came in second, when one of these 10 parties or alliances won the mayor race in a specific canton. In this point, I use a second dataset with the total results in each city council in the country to identify the party that came in second. It is important to note that each political party or alliance present one candidate in each canton. Therefore, I use the id for candidates to identify the number of votes to later join the data with the id for parties.

# loading data of total results
load("results.Rdata")
head(resultados_alc)
## # A tibble: 6 x 6
##   PROVINCIA_CODIGO PROVINCIA_NOMBRE CANTON_CODIGO CANTON_NOMBRE CANDIDATO_CODIGO
##              <dbl> <fct>                    <dbl> <fct>                    <dbl>
## 1                1 Azuay                      260 Cuenca                   13581
## 2                1 Azuay                      260 Cuenca                   15411
## 3                1 Azuay                      260 Cuenca                   15881
## 4                1 Azuay                      260 Cuenca                   22636
## 5                1 Azuay                      260 Cuenca                   24094
## 6                1 Azuay                      260 Cuenca                   24778
## # ... with 1 more variable: VOTOS <dbl>
# finding parties in second place for each victory of top parties
second_party <- resultados_alc %>%
  arrange(CANTON_CODIGO, desc(VOTOS)) %>% #CANTON_CODIGO - id for city council
  group_by(CANTON_CODIGO) %>%
  mutate(order = row_number()) %>% #creating index 
  filter(order <= 2) %>% select(-VOTOS, -PROVINCIA_CODIGO, -PROVINCIA_NOMBRE) %>%  #filtering out first and second party
  left_join(partidos_alc %>% #getting info on parties using shared variable (id for candidate)
              select(CANDIDATO_CODIGO, OP_SIGLAS), by = "CANDIDATO_CODIGO") %>%
  rename(OPS = OP_SIGLAS) %>%
  mutate(OP_SIGLAS = ifelse(order == 1, as.character(OPS), "")) %>%
  left_join(top_parties, by = "OP_SIGLAS") %>% #it can't be right join because it would remove order == 2
  mutate(n = ifelse(is.na(n) == TRUE, 0, n),
         temp = sum(n)) %>% ungroup() %>% 
  filter(temp != 0) %>%
  mutate(sec = ifelse(order == 1, as.character(lead(OPS)), NA)) %>% #creates variable with party in second place
  select(-OPS, -temp) %>%
  filter(!is.na(sec)) %>%
  count(OP_SIGLAS, n, sec)

second_party %>%
  group_by(OP_SIGLAS) %>%
  filter(nn == max(nn)) %>%
  arrange(desc(n), nn)
## # A tibble: 19 x 4
## # Groups:   OP_SIGLAS [10]
##    OP_SIGLAS                    n sec           nn
##    <chr>                    <dbl> <chr>      <int>
##  1 MUPP                        15 MPFA/MPAIS     3
##  2 PSC/MMDG                    14 MPAIS          4
##  3 MPAIS                       10 PSC            2
##  4 PSC                          9 MPAIS          2
##  5 PSC                          9 UP             2
##  6 CREO                         9 MPAIS          3
##  7 MDSI                         8 PSC            2
##  8 SUMA                         8 MUPP           2
##  9 PSC/MPUP                     6 CREO/MSP       3
## 10 CREO/MCUP/PAEA/FE/UE/MNP     5 APLA/MPQ       1
## 11 CREO/MCUP/PAEA/FE/UE/MNP     5 ID             1
## 12 CREO/MCUP/PAEA/FE/UE/MNP     5 MJS/PSE        1
## 13 CREO/MCUP/PAEA/FE/UE/MNP     5 PSC            1
## 14 CREO/MCUP/PAEA/FE/UE/MNP     5 SUMA           1
## 15 MEU/MDSI                     5 MCR            1
## 16 MEU/MDSI                     5 MGG            1
## 17 MEU/MDSI                     5 MNP/MPDR       1
## 18 MEU/MDSI                     5 MPAIS          1
## 19 MEU/MDSI                     5 MUPP           1

There is no single clear contender for the parties with the best performance. For instance, the MPFA/MPAIS came in second three times when the MUPP won, which represents 20% of the victories for the MUPP. Most of these parties came in second less than 50% of the times when the top parties won. This gives further evidence of the level of fragmentation in the political system.

# parties that appear the most in second place
second_party %>%
  group_by(sec) %>%
  mutate(total_sec = sum(nn)) %>%
  select(-OP_SIGLAS, -n, -nn) %>% unique() %>%
  arrange(desc(total_sec)) %>%
  head(20)
## # A tibble: 20 x 2
## # Groups:   sec [20]
##    sec                      total_sec
##    <chr>                        <int>
##  1 MPAIS                           12
##  2 PSC                              6
##  3 MUPP                             5
##  4 CREO                             4
##  5 PSC/MMDG                         3
##  6 UP                               3
##  7 MPFA/MPAIS                       3
##  8 CREO/MSP                         3
##  9 SUMA                             2
## 10 MNP/MPDR                         2
## 11 CD                               2
## 12 CREO/MCUP/PAEA/FE/UE/MNP         2
## 13 SUMA/PSC                         2
## 14 PSC/SUMA                         2
## 15 MFCS                             2
## 16 MLC                              1
## 17 MUPP/PRIMIZA                     1
## 18 PPA                              1
## 19 APLA/MPQ                         1
## 20 ID                               1

The table shows the 20 first parties that came in second when the top ten parties won. MPAIS is the party that came in second the most. It would appear that single parties, in this specific case, had a better performance than alliances, since they are represented by the first four positions with the highest values.

Difference between parties in first and second place

# getting difference between first and second parties when top parties won
fsp_results <- resultados_alc %>%
  select(-PROVINCIA_CODIGO, -PROVINCIA_NOMBRE) %>%
  group_by(CANTON_CODIGO) %>% 
  mutate(total = sum(VOTOS),
         perc = (VOTOS/total)*100) %>%
  arrange(CANTON_CODIGO, desc(VOTOS)) %>%
  mutate(order = row_number()) %>%
  filter(order <= 2) %>%
  mutate(dif_seg = perc - lead(perc),
         CANTON_NOMBRE = as.character(CANTON_NOMBRE)) %>% #CANTON_NOMBRE - name of city council
  filter(!is.na(dif_seg)) %>%
  left_join(partidos_alc %>% select(-OP_NOMBRE, -CANDIDATO_ESTADO), by = "CANDIDATO_CODIGO") %>%
  right_join(top_parties %>% ungroup() %>% mutate(party_n = row_number()), by = "OP_SIGLAS") %>%
  select(-order, -perc, -VOTOS, -total, -CANDIDATO_CODIGO) %>%
  arrange(desc(n), desc(dif_seg))

fsp_plot <- plot_ly(fsp_results, x = ~reorder(CANTON_NOMBRE, party_n), y = ~round(dif_seg,2), type = 'bar', 
                    color = ~as.factor(OP_SIGLAS), 
                    colors = 'Set2') %>%
  layout(title = 'Difference  between first and second parties in each canton for top parties',
         legend = list(orientation = 'v',
                       xanchor = 'left',
                       x = 1.00,
                       y = 0.35,
                       traceorder = 'normal'),
         yaxis = list(title = 'Percent'),
         xaxis = list(title = ''))
fsp_plot

From the 89 victories from the top parties, ten had a difference over 20% of the votes with the second party. These cases would suggest a wide support from the electorate. However, by looking at individual cases, it is possible to identify how party performance is related to the number of candidates running for mayor at each city council.

# Canton: A. Baquerizo Moreno
ABM <- arrange(resultados_alc[which(resultados_alc$CANTON_NOMBRE=='A.baquerizo Moreno'),], desc(VOTOS)) %>%
  select(-PROVINCIA_CODIGO, -PROVINCIA_NOMBRE, -CANTON_CODIGO)
ABM
## # A tibble: 9 x 3
##   CANTON_NOMBRE      CANDIDATO_CODIGO VOTOS
##   <fct>                         <dbl> <dbl>
## 1 A.baquerizo Moreno             3576  7793
## 2 A.baquerizo Moreno             6576  1981
## 3 A.baquerizo Moreno            30928  1937
## 4 A.baquerizo Moreno            34716  1492
## 5 A.baquerizo Moreno            22648  1157
## 6 A.baquerizo Moreno             9839   522
## 7 A.baquerizo Moreno            21276   418
## 8 A.baquerizo Moreno             9487   184
## 9 A.baquerizo Moreno            29092   103
round((max(ABM$VOTOS)/sum(ABM$VOTOS))*100,3)
## [1] 49.997
# Canton: Daule
DAU <- arrange(resultados_alc[which(resultados_alc$CANTON_NOMBRE=='Daule'),], desc(VOTOS)) %>%
  select(-PROVINCIA_CODIGO, -PROVINCIA_NOMBRE, -CANTON_CODIGO)
DAU
## # A tibble: 18 x 3
##    CANTON_NOMBRE CANDIDATO_CODIGO VOTOS
##    <fct>                    <dbl> <dbl>
##  1 Daule                     1162 28472
##  2 Daule                    21019 10686
##  3 Daule                    32938  7253
##  4 Daule                    12817  6000
##  5 Daule                    17590  4881
##  6 Daule                    20742  4847
##  7 Daule                     6387  3880
##  8 Daule                    34926  3105
##  9 Daule                     7717  2447
## 10 Daule                    24656  2337
## 11 Daule                     4718  1895
## 12 Daule                    20016  1390
## 13 Daule                    23086  1373
## 14 Daule                    38020   598
## 15 Daule                    29178   490
## 16 Daule                    33543   444
## 17 Daule                    36569   324
## 18 Daule                    24318   255
round((max(DAU$VOTOS)/sum(DAU$VOTOS))*100,3)
## [1] 35.291
# Canton: Duran
DUR <- arrange(resultados_alc[which(resultados_alc$CANTON_NOMBRE=='Duran'),], desc(VOTOS)) %>%
  select(-PROVINCIA_CODIGO, -PROVINCIA_NOMBRE, -CANTON_CODIGO)
DUR
## # A tibble: 21 x 3
##    CANTON_NOMBRE CANDIDATO_CODIGO VOTOS
##    <fct>                    <dbl> <dbl>
##  1 Duran                     3455 47621
##  2 Duran                     9466 33230
##  3 Duran                     4423 30987
##  4 Duran                    16883  7264
##  5 Duran                    11645  4931
##  6 Duran                    20092  2511
##  7 Duran                    32630  2142
##  8 Duran                    20490  1762
##  9 Duran                    21873  1513
## 10 Duran                    35634  1264
## # ... with 11 more rows
round((max(DUR$VOTOS)/sum(DUR$VOTOS))*100,3)
## [1] 34.297

In the first case, the party that won in A. Baquerizo Moreno got 49.99% of the votes. Eight other parties were participating in this mayor race. However, in Daule, the winning party got 35.29% of the vote in an election with 18 candidates. Another canton, Duran, had 21 candidates, where the winner got little more than a third of the total votes (34,29%).

These few examples point to the existence of fragmentation in the political system. Moreover, the widespread use of alliances has not diminished the appearance of wide range of political organizations. In this case, the main issue with fragmentation has to do with the legitimacy of the party in power. Being elected with less than half of support in a scenario with more than ten political parties could potentially constrained the party performance once in power.

Future Work

This is small exploratory analysis that could serve as a basis for further research. In this sense, it would be interesting to explore difference at geographical level to check if party fragmentation is concentrated over certain regions. The analysis only took into account the number of victories, but it would also be interesting to determine performance based on the number of votes, since this would also control for population size. The dataset has the number of votes as well. Finally, it would be possible to establish the likelihood of victory of each party, based on its participation in certain locations and measuring its performance compared to other parties.